package com.tyk.bookstore.back.order.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tyk.bookstore.back.common.core.model.page.PageVo;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.logistics.model.query.WayBill4OrderAddQuery;
import com.tyk.bookstore.back.logistics.service.ExpressCompanyApiService;
import com.tyk.bookstore.back.logistics.service.WaybillApiService;
import com.tyk.bookstore.back.member.service.DeliveryAddressApiService;
import com.tyk.bookstore.back.member.service.MemberApiService;
import com.tyk.bookstore.back.order.manager.excel.model.OrderExcelVo;
import com.tyk.bookstore.back.order.model.dto.OrderBriefDto;
import com.tyk.bookstore.back.order.model.dto.OrderCneeAddressDto;
import com.tyk.bookstore.back.order.model.dto.OrderExplicitDto;
import com.tyk.bookstore.back.order.model.entity.OrderDetailDo;
import com.tyk.bookstore.back.order.model.entity.OrderDo;
import com.tyk.bookstore.back.order.model.entity.OrderExtraDo;
import com.tyk.bookstore.back.order.model.enume.OrderState;
import com.tyk.bookstore.back.order.model.query.CneeAddressUpdateQuery;
import com.tyk.bookstore.back.order.model.query.OrderDispatchQuery;
import com.tyk.bookstore.back.order.model.query.OrderPageQuery;
import com.tyk.bookstore.back.order.model.result.OrderResultEnum;
import com.tyk.bookstore.back.order.model.vo.OrderCneeAddressVo;
import com.tyk.bookstore.back.order.model.vo.OrderPageVo;
import com.tyk.bookstore.back.order.model.vo.OrderVo;
import com.tyk.bookstore.back.order.repository.OrderDetailRepository;
import com.tyk.bookstore.back.order.repository.OrderExtraRepository;
import com.tyk.bookstore.back.order.repository.OrderRepository;
import com.tyk.bookstore.back.order.service.OrderService;
import com.tyk.bookstore.back.order.util.MapStructOrder;
import com.tyk.bookstore.back.platform.service.AreaApiService;
import com.tyk.bookstore.back.storage.model.dto.WarehouseNameDto;
import com.tyk.bookstore.back.storage.service.WarehouseApiService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author tyk
 * @description 订单管理
 */
@Service
@RequiredArgsConstructor
public class OrderServiceImp implements OrderService {

    private final OrderRepository orderRepository;
    private final OrderExtraRepository orderExtraRepository;
    private final OrderDetailRepository orderDetailRepository;
    private final AreaApiService areaApiService;
    private final MemberApiService memberApiService;
    private final WarehouseApiService warehouseApiService;
    private final DeliveryAddressApiService deliveryAddressApiService;
    private final ExpressCompanyApiService expressCompanyApiService;
    private final WaybillApiService waybillApiService;
    private final MapStructOrder mapStructOrder;

    @Override
    public OrderVo getById(Long id) {

        // 订单核心信息
        OrderDo od = orderRepository.selectById(id);
        AssertUtil.notNull(od, OrderResultEnum.ORDER_NOT_FOUND);
        // 订单额外信息
        OrderExtraDo oed = orderExtraRepository.selectById(id);

        // 转换
        OrderVo ov = mapStructOrder.toOrderVo(od, oed);

        // 会员昵称
        String nickname = memberApiService.getNicknameById(ov.getMemberId());
        ov.setMemberName(nickname);

        // 收件人区域
        String cneeAreaFullName = areaApiService.getFullNameListByCodeSet(Set.of(ov.getCneeAreaCode())).get(0).getValue();
        ov.setCneeAreaFullName(cneeAreaFullName);

        // 已拆单情况
        if (od.getState() == OrderState.SPLIT) {
            LambdaQueryWrapper<OrderDo> w = Wrappers.lambdaQuery(OrderDo.class);
            w.select(OrderDo::getId, OrderDo::getOrderNo, OrderDo::getAmount, OrderDo::getState)
                    .eq(OrderDo::getParentId, id);
            List<OrderDo> subOrderList = orderRepository.selectList(w);
            ov.setChildList(
                    mapStructOrder.toOrderVoList$Order(subOrderList)
            );
        }
        // 未拆单情况
        else {
            LambdaQueryWrapper<OrderDetailDo> w = Wrappers.lambdaQuery(OrderDetailDo.class);
            w.select(
                    OrderDetailDo::getBookId,
                    OrderDetailDo::getBookNo,
                    OrderDetailDo::getCover,
                    OrderDetailDo::getTitle,
                    OrderDetailDo::getPrice,
                    OrderDetailDo::getNum
            ).eq(OrderDetailDo::getOrderId, id);
            List<OrderDetailDo> detailList = orderDetailRepository.selectList(w);
            ov.setDetailList(
                    mapStructOrder.toOrderVo$OrderDetailList(detailList)
            );
        }

        return ov;
    }

    @Override
    public OrderPageVo getPage(OrderPageQuery query) {

        // 符合条件的简略订单
        LambdaQueryWrapper<OrderDo> wrapper = buildOrderQueryWrapper(query);
        Map<Long, List<OrderBriefDto>> briefMap = orderRepository
                .selectOrderBriefDtoList(wrapper)
                .stream().collect(Collectors.groupingBy(OrderBriefDto::getParentId));

        // 特判
        if (ObjUtil.isEmpty(briefMap)) return PageVo.instance(OrderPageVo.class);

        // 符合条件的简略订单的父订单的ID
        Set<Long> pidSet = new HashSet<>(briefMap.keySet());

        // 去除已查询到的父订单的ID
        if (briefMap.containsKey(0L))
            briefMap.get(0L).stream().map(OrderBriefDto::getId).toList().forEach(pidSet::remove);

        // 补充遗漏的父订单简略信息
        List<OrderBriefDto> parentBriefList = orderRepository.selectOrderBriefDtoListByIdList(pidSet);
        if (briefMap.containsKey(0L)) {
            parentBriefList.addAll(briefMap.get(0L));
        }

        // 分页参数
        int fromIndex = (int) ((query.getPage() - 1) * query.getLimit());
        int total = parentBriefList.size();
        if (fromIndex >= total) {
            fromIndex = 0;
            query.setPage(1L);
        }
        int toIndex = (int) Math.min(fromIndex + query.getLimit(), total);

        // 父订单按时间排序
        parentBriefList.sort(Comparator.comparing(OrderBriefDto::getCreateTime, Comparator.reverseOrder()));

        // 订单分页
        List<OrderBriefDto> parentBriefSubList = parentBriefList.subList(fromIndex, toIndex);

        // 需要查询详情的订单ID
        HashSet<Long> needSelectIdSet = new HashSet<>();
        parentBriefSubList.forEach(order -> {
            Long id = order.getId();
            needSelectIdSet.add(id);
            if (briefMap.containsKey(id)) {
                needSelectIdSet.addAll(briefMap.get(id).stream().map(OrderBriefDto::getId).toList());
            }
        });

        // 订单核心信息
        List<OrderExplicitDto> explicitList = orderRepository.selectOrderExplicitDtoListByIdSet(needSelectIdSet);
        Map<Long, List<OrderExplicitDto>> explicitMap = explicitList.stream().collect(
                Collectors.groupingBy(OrderExplicitDto::getParentId)
        );

        // 订单核心信息
        Map<Long, OrderExplicitDto> ocMap = explicitList.stream().collect(Collectors.toMap(
                OrderExplicitDto::getId,
                it -> it
        ));

        // 订单额外信息
        Map<Long, OrderExplicitDto> oeMap = orderExtraRepository.selectOrderExplicitDtoListByIdSet(needSelectIdSet)
                .stream().collect(Collectors.toMap(
                        OrderExplicitDto::getId,
                        it -> it
                ));

        // 仓库名称
        Map<Long, WarehouseNameDto> wnMap = warehouseApiService
                .getWarehouseNameDtoListByIdSet(
                        explicitList.stream().map(OrderExplicitDto::getWarehouseId).collect(Collectors.toSet())
                )
                .stream().collect(Collectors.toMap(WarehouseNameDto::getId, it -> it));

        // 转Vo
        // parentBriefSubList 等价于 explicitMap.get(0L)
        // parentBriefSubList经过排序
        List<OrderPageVo.Order> records = parentBriefSubList.stream().map(po -> {
            OrderPageVo.Order ov = mapStructOrder.toOrderPageVo$Order(
                    ocMap.get(po.getId()), oeMap.get(po.getId())
            );
            // 含有子订单 且 已查出的订单中含有该订单的子订单
            if (ov.getState() == OrderState.SPLIT && explicitMap.containsKey(ov.getId())) {
                List<OrderPageVo.Order> subList = explicitMap.get(ov.getId()).stream().map(sub -> {
                    OrderPageVo.Order sov = mapStructOrder.toOrderPageVo$Order(sub, oeMap.get(sub.getId()));
                    sov.setWarehouseName(
                            wnMap.get(sub.getWarehouseId()).getName()
                    );
                    return sov;
                }).toList();
                ov.setChildren(subList);
            }
            // 待支付、取消支付、已拆单三种状态对应的订单的warehouseId为null
            if (ov.getWarehouseId() != 0L) {
                ov.setWarehouseName(
                        wnMap.get(ov.getWarehouseId()).getName()
                );
            }
            return ov;
        }).toList();

        //返回数据
        OrderPageVo orderPageVo = new OrderPageVo();
        orderPageVo.setCurrent(query.getPage()).setSize(query.getLimit()).setTotal((long) total).setRecords(records);
        return orderPageVo;
    }

    @Override
    public void dispatch(OrderDispatchQuery query) {

        // 快递公司存在
        expressCompanyApiService.assertExistById(query.getExpressCompanyId());

        // 订单存在
        OrderDo order = orderRepository.selectById(query.getOrderId());
        AssertUtil.notNull(order, OrderResultEnum.ORDER_NOT_FOUND);
        OrderCneeAddressDto orderAddress = orderExtraRepository.selectOrderCneeAddressDtoByOrderId(query.getOrderId());

        // 派单对象
        WayBill4OrderAddQuery wq = mapStructOrder.toWayBill4OrderAddQuery(query, order, orderAddress);

        // 派单
        Long waybillId = waybillApiService.generateWaybill(wq);

        // 更新订单信息
        orderRepository.update2Set1Eq(
                OrderDo::getState, OrderState.WAIT_TAKE,
                OrderDo::getWaybillId, waybillId,
                OrderDo::getId, order.getId(),
                OrderDo.class
        );
        orderExtraRepository.update1Set1Eq(
                OrderExtraDo::getDispatchTime, LocalDateTime.now(),
                OrderExtraDo::getOrderId, order.getId(),
                OrderExtraDo.class
        );

    }

    @Override
    public void deleteById(Long id) {
        orderRepository.deleteById(id);
    }

    @Override
    public void deleteBatchByIdSet(Set<Long> idSet) {
        orderRepository.deleteBatchIds(idSet);
    }

    @Override
    public void updateCneeAddress(CneeAddressUpdateQuery query) {

        // 仅待发货订单可以修改收获地址
        AssertUtil.isTrue(
                orderRepository.exist2Eq(
                        OrderDo::getId, query.getOrderId(),
                        OrderDo::getState, OrderState.WAIT_SEND,
                        OrderDo.class
                ),
                OrderResultEnum.ORDER_ADDRESS_UPDATE_NOT_ALLOWED
        );

        // 修改收货地址信息
        OrderExtraDo oed = mapStructOrder.toOrderExtraDo(query);
        orderExtraRepository.updateById(oed);

    }

    @Override
    public OrderCneeAddressVo getCneeAddressByOrderId(Long orderId) {
        AssertUtil.isTrue(
                orderRepository.exist1Eq(
                        OrderDo::getId, orderId, OrderDo.class
                ),
                OrderResultEnum.ORDER_NOT_FOUND
        );
        OrderCneeAddressDto dto = orderExtraRepository.selectOrderCneeAddressDtoByOrderId(orderId);
        if (dto == null) dto = new OrderCneeAddressDto();
        dto.setOrderId(orderId);
        return mapStructOrder.toOrderCneeAddressVo(dto);
    }

    @Override
    public List<OrderExcelVo> getOrderExcelVoList(OrderPageQuery query) {
        LambdaQueryWrapper<OrderDo> w = buildOrderQueryWrapper(query);
        List<OrderDo> doList = orderRepository.selectList(w);
        Map<Long, OrderExtraDo> oeMap = orderExtraRepository.selectList(
                Wrappers.lambdaQuery(OrderExtraDo.class).in(
                        OrderExtraDo::getOrderId,
                        doList.stream().map(OrderDo::getId).toList()
                )
        ).stream().collect(Collectors.toMap(
                OrderExtraDo::getOrderId, it -> it
        ));
        return doList.stream().map(it -> mapStructOrder.toOrderExcelVo(it, oeMap.get(it.getId()))).toList();
    }

    /**
     * 构建订单分页查询Wrapper
     */
    private LambdaQueryWrapper<OrderDo> buildOrderQueryWrapper(OrderPageQuery query) {

        LambdaQueryWrapper<OrderDo> w = Wrappers.lambdaQuery(OrderDo.class);

        // 订单额外信息条件
        LambdaQueryWrapper<OrderExtraDo> we = Wrappers.lambdaQuery(OrderExtraDo.class);
        we.eq(query.getPaymentWay() != null, OrderExtraDo::getPaymentWay, query.getPaymentWay());
        we.eq(ObjUtil.notBlank(query.getCneeName()), OrderExtraDo::getCneeName, query.getCneeName());
        we.eq(ObjUtil.notBlank(query.getCneePhone()), OrderExtraDo::getCneePhone, query.getCneePhone());
        we.eq(query.getCneeAreaCode() != null, OrderExtraDo::getCneeAreaCode, query.getCneeAreaCode());
        we.select(OrderExtraDo::getOrderId);
        Set<Long> ids1 = orderExtraRepository.selectList(we).stream().map(OrderExtraDo::getOrderId).collect(Collectors.toSet());
        if (ObjUtil.isEmpty(ids1)) {
            w.eq(OrderDo::getId, -1L);
            return w;
        }

        // 书籍信息条件
        if (ObjUtil.notBlank(query.getBookNo())) {
            Set<Long> ids2 = orderDetailRepository
                    .<BigInteger>selectSinList1Like(
                            OrderDetailDo::getOrderId,
                            OrderDetailDo::getBookNo, query.getBookNo(),
                            OrderDetailDo.class
                    ).stream().map(it -> it.getV1().longValue()).collect(Collectors.toSet());
            if (ObjUtil.isEmpty(ids2)) {
                w.eq(OrderDo::getId, -1L);
                return w;
            }
            ids1.retainAll(ids2);
        }

        if (ObjUtil.isEmpty(ids1)) {
            w.eq(OrderDo::getId, -1L);
            return w;
        }

        // 运单条件
        if (ObjUtil.notBlank(query.getOrderNo())) {
            Long waybillId = waybillApiService.getIdByTrackingNo(query.getOrderNo());
            if (waybillId == null) {
                w.eq(OrderDo::getId, -1L);
                return w;
            }
            w.eq(OrderDo::getWaybillId, waybillId);
        }

        // 订单条件
        w.in(OrderDo::getId, ids1);

        w.eq(ObjUtil.notBlank(query.getOrderNo()), OrderDo::getOrderNo, query.getOrderNo());

        w.eq(query.getState() != null, OrderDo::getState, query.getState());

        w.ge(query.getAmountFrom() != null, OrderDo::getAmount, query.getAmountFrom());
        w.le(query.getAmountTo() != null, OrderDo::getAmount, query.getAmountTo());

        w.eq(query.getWarehouseId() != null, OrderDo::getWarehouseId, query.getWarehouseId());

        w.ge(query.getCreateTimeFrom() != null, OrderDo::getCreateTime, query.getCreateTimeFrom());
        w.le(query.getCreateTimeTo() != null, OrderDo::getCreateTime, query.getCreateTimeTo());

        w.ge(query.getPaymentTimeFrom() != null, OrderDo::getCreateTime, query.getPaymentTimeFrom());
        w.le(query.getPaymentTimeTo() != null, OrderDo::getCreateTime, query.getPaymentTimeTo());

        return w;
    }
}
